/** @file widget.cpp
 * @brief base class of fgui
 * @author    - Adela 
 *            - Robert Zhang <armner@gmail.com>
 *            - Wismy Yao<aheynie@gmail.com>
 * @date 2013-05
 * @note
 */

#include "widget.h"
#include "message.h"
#include "list.h"
#include "stdlib.h"
#include "msg_queue.h"
#include "dc.h"
#include "stdio.h"

void __cxa_pure_virtual(void)
{} 

/** @brief constructor of a widget
 * @note  setup tree\n create a DC\n size &location default to 0.
 */
Widget::Widget(HWND hWnd)
{
    childList =NUL;
    prev =NUL;
    next =NUL;
    activeChild =NUL;
    
    activable =0;
    ///active =0;
    lifeState =0;

    pDC =new DC(this);
    if(hWnd)
    {
        setParent(hWnd);

        rect.x0 =rect.y0 =0;
        rect.x1 =rect.y1 =0;
    }
    else
    {
        rect.x0 = rect.y0=0;
        rect.x1 = rect.y1=0;
    
        parent =NUL;
    }

}

/** @brief deconstruct of a widget
 * @note unlink to a tree
 */
Widget::~Widget()
{
    if(parent)
    {
        unlinkParent();
    }
    delete pDC;
}

/** @brief  operator 'delete' definitions
 * @param void *t
 * @note 
 */
void Widget::operator delete(void *t)
{
    port_free(t);
}
/** @brief operator 'new' definitions
 * @param unsigned int len
 * @note 
 */
void *Widget::operator new(unsigned int len)
{
    return port_malloc(len);
}

/** @brief window draw
 * @param Rect *r
 * @note 
 */
void Widget::draw(Rect *r)
{
#ifdef DEBUG
     printf("window %s draw: w=%d, h=%d\n", name, r->x1-r->x0, r->y1-r->y0);
#endif
}

/** @brief reset  DC
 * @note 
 */
 void Widget::resetDC()
{
    pDC->cleanIdleRect();
}

/** @brief release DC
 * @note first reset DC\n return rect to parent window
 */
 void Widget::releaseDC()
{
#ifdef DEBUG_DC
    printf("window %s releaseDC: %x\n", name, &rect);
#endif

    //resetDC();
    
    if(parent)
    {
        sendMessage(parent, MSG_PASTE_RECT, (WPARAM)&rect,0);
    }
}

/** @brief setup DC
 * @note cut a rect from parent window
 */
void Widget::setupDC()
{
#ifdef DEBUG_DC
    printf("window %s setupDC: ", name, &rect);
    printf("        parent is %x, name:%s \n", parent, parent->name );
#endif
    if(parent)
    {
        sendMessage(parent,  MSG_CUT_RECT, (WPARAM)&rect, (LPARAM)this);
    }

}

/** @brief change position to a new location
 * @param short x
 * @param short y
 * @note
 * release DC first \n
 * set to new location \n
 * setupDC to the new location \n
 * set all children to the new location and re-setupDC 
 */
void Widget::setPosition(short x, short y)
{
    //rect changed
    //if( (lifeState>0) && sizew() && sizeh())
    //{
    //    releaseDC();
    //}

    rect.x0 +=x;
    rect.y0 +=y;
    rect.x1 +=x;
    rect.y1 +=y;

    if(parent)
    {
        rect.x0 =max(rect.x0, parent->rect.x0);
        rect.y0 =max(rect.y0, parent->rect.y0);
        rect.x1 =min(rect.x1, parent->rect.x1);
        rect.y1 =min(rect.y1, parent->rect.y1);
    }

    //if( (lifeState>0) && sizew() && sizeh())
    //{
    //    setupDC();
    //}

    sendChildMessage(this, MSG_SET_POSITION, x, y);
}

/** @brief reset size
 * @param short w
 * @param short h
 * @note
 * release DC first \n
 * change size \n
 * re-setup DC \n
 * due to size changed, all children also re-setupDC
 */
void Widget::setSize(short w, short h)
{
    //if( (lifeState>0) && sizew() && sizeh())
    //{
    //    releaseDC();
    //}

    rect.x1 =rect.x0+w;
    rect.y1 =rect.y0+h;

    if(parent)
    {
        rect.x0 =max(rect.x0, parent->rect.x0);
        rect.y0 =max(rect.y0, parent->rect.y0);
        rect.x1 =min(rect.x1, parent->rect.x1);
        rect.y1 =min(rect.y1, parent->rect.y1);
    }

    //if((lifeState>0) && sizew() && sizeh())
    //{
    //    setupDC();
    //}

    //sendChildMessage(this, MSG_SET_POSITION, 0,0);
}

/** @brief window create
 * @note when create, setupDC\n all children Create
 */
void Widget::create()
{
#ifdef DEBUG_FRGUI
        printf("window %s creating\n", name);
#endif
    lifeState =1;
    setupDC();
    draw(&rect);
    sendChildMessage(this, MSG_CREATE, 0,0);
}


/** @brief widget is dying
 * @note window is dying\n release DC,send\n all children to dead 
 */
void Widget::close()
{
#ifdef DEBUG_FRGUI
        printf("window %s destroying\n", name);
#endif

    resetDC();
    sendChildMessage(this, MSG_CLOSE,0,0);

    lifeState =0;
    releaseDC();

    unlinkParent();
    setPosition(-rect.x0, -rect.y0);


}

/** @brief default handler for all widgets
 * @param HWND hWnd
 * @param int iMsg
 * @param WPARAM wParam
 * @param LPARAM lParam
 * @note 
 */
int Widget::defaultHandler(HWND hWnd, int iMsg, WPARAM wParam, LPARAM lParam)
{
    int ret_val =0;
    
    switch( iMsg)
    {
         
    case MSG_CREATE://< when create, setupDC\n all children Create
        create();
        break;
        
        
    case MSG_CLOSE://< window is dying\n release DC\n send all children to dead
        close();
        break;
        
         
    case MSG_ADD_CHILD://< add a child to child linklist tail
        addChild( (HWND)wParam);
        break;
        
        
    case MSG_DEL_CHILD://< remove a child from linklist
        removeChild( (HWND)wParam);
        break;

    case MSG_SET_POSITION:
        setPosition( (short)wParam, (short)lParam);
        break;
            
          
    case MSG_PAINT: //< paint     
        if(lifeState >0)
        {
#ifdef DEBUG_FRGUI
     printf("window %s draw\n", name);
#endif
            draw(&rect);
            sendChildMessage(hWnd, iMsg, wParam, lParam);
        }
        break;

        
    case MSG_TICK://< tick
        sendChildMessage(hWnd, iMsg, wParam, lParam);
        break;

        
    case MSG_CUT_RECT://< cut a rect from DC
        if(wParam == (WPARAM)&rect)
        {
        }
        else
        {
            pDC->cutRect( (Rect *) wParam, (HWND)lParam);
            sendChildMessage(hWnd, MSG_CUT_RECT, wParam,lParam);
        }
        break;

        
    case MSG_PASTE_RECT://< paste a rect to a DC
        if(wParam ==(WPARAM)&rect)
        {
        }
        else
        {
            if(pDC->pasteRect( (Rect *)wParam))
            {
                draw((Rect*)wParam);
            }
            sendChildMessage(hWnd, MSG_PASTE_RECT, wParam, lParam);
        }
        break;

    case MSG_RESET_DC:
        resetDC();
        sendChildMessage(hWnd, iMsg, wParam, lParam);
        break;

/*    case MSG_SETUP_DC:
        setupDC();
        sendChildMessage(hWnd, iMsg, wParam, lParam)
        break;
*/
    }
        
    ret_val =eventHandler(hWnd, iMsg, wParam, lParam);

    return ret_val;

}


/** @brief widget normal event handler
 * @param HWND hWnd
 * @param int iMsg
 * @param WPARAM wParam
 * @param LPARAM lParam
 * @note 
 * if(KEY_PREV) activate elder brother\n
 * if(KEY_NEXT) activate younger brother
 */
int Widget::eventHandler(HWND hWnd, int iMsg, WPARAM wParam, LPARAM lParam)
{

    switch(iMsg)
    {
    case MSG_KEY_DOWN:
        switch(wParam)
        {
        case KEY_ENTER:
            activeFirstChild();
            break;
        case KEY_PREV:
            #ifdef DEBUG_FRGUI
            printf("window %s active elder brother\n", name);
            #endif
            sendMessage(parent, MSG_ACTIVE_ELDER_CHILD, 0,0);
            leaveFocus();
            break;
                
        case KEY_NEXT:
            #ifdef DEBUG_FRGUI
            printf("window %s active younger brother\n", name);
            #endif
            sendMessage(parent, MSG_ACTIVE_YOUNGER_CHILD, 0,0);
            leaveFocus();
            break;
        }
        break;
    }
    
    return 0;
}

/** @brief make the first child active
 * @note 
 */
void Widget::activeFirstChild()
{
    HWND hWnd;

    for(hWnd =getFirstItem(&childList); hWnd; hWnd =hWnd->next)
    {
        if(hWnd->activable)
        {
            if(hWnd == activeChild)
                return;
            else
            {
                HWND prev_active =activeChild;
                activeChild =hWnd;
                deActivateWidget(prev_active);
                activateWidget(activeChild);

                break;
            }
            
        }
    }
}

/** @brief make the younger brother active 
 * @note 
 */
void Widget::activeNextChild()
{
    HWND hWnd;
    
    if(1)
    {
        if(activeChild)
        {
            
            hWnd =activeChild->next;//< find the last activable younger child
            
            while(hWnd)
            {
                if(hWnd->activable)
                {
                    HWND prev_active =activeChild;
                    activeChild =hWnd;
                    deActivateWidget(prev_active);
                    activateWidget(hWnd);

                    //rect
                    //hWnd->releaseDC();
                    //hWnd->setupDC();
                    //sendMessage(hWnd, MSG_PAINT, 0,0);

                    break;
                }
                hWnd =hWnd ->next;
            }
            
            
            if(! hWnd)//< find no activable brother, active parent
            {
                if(activable)
                {
                    HWND prev_active=activeChild;
                    activeChild =0;
                    deActivateWidget(prev_active);
                    activateWidget(this);
                }
            }
        }
        
        
        else//< find the first activale child
        {
            hWnd =getFirstItem(&childList);
            while(hWnd)
            {
                if(hWnd->activable)
                {
                    activeChild = hWnd;
                    activateWidget(activeChild);

                    //rect
                    //hWnd->releaseDC();
                    //hWnd->setupDC();
                    //sendMessage(hWnd, MSG_PAINT, 0,0);
                    break;
                }
                hWnd =hWnd->next;
            }    
        }
    }
}

/** @brief make the elder brother active
 * @note  
 */
void Widget::activePrevChild()
{
    HWND hWnd;
    
    if(1)
    {
        if(activeChild)
        {
            
            hWnd =activeChild->prev;//< find the last activable younger child
            
            while(hWnd)
            {
                if(hWnd->activable)
                {
                    HWND prev_active =activeChild;
                    activeChild =hWnd;
                    deActivateWidget(prev_active);
                    activateWidget(activeChild);

                    //rect
                    //hWnd->releaseDC();
                    //hWnd->setupDC();
                    //sendMessage(hWnd, MSG_PAINT, 0,0);
                    break;
                }
                hWnd =hWnd ->prev;
            }
            
            
            if(! hWnd)//< find no activable brother, active parent
            {
                if(activable)
                {
                    HWND prev_active =activeChild;
                    activeChild =0;
                    deActivateWidget(prev_active);
                    activateWidget(this);
                }
            }
        }
        
    }
}


/** @brief send message to children 
 * @param HWND hWnd
 * @param int iMsg
 * @param WPARAM wParam
 * @param LPARAM lParam
 * @note to prevent child tree lost\n firstly get the next child.
 */
void Widget::sendChildMessage(HWND hWnd, int iMsg, WPARAM wParam, LPARAM lParam)
{
    HWND t_widget;
    HWND t_widget1;

    t_widget1 =getFirstItem(&childList);
    
    while(t_widget1)
    {
        t_widget =t_widget1;
        t_widget1 =t_widget1->next;
        sendMessage(t_widget, iMsg, wParam, lParam);
    }
}

/** @brief add to parent's child list
 * @param HWND pWidget
 * @note 
 */
void Widget::setParent(HWND pWidget)
{
    parent =pWidget;
    if(pWidget)
    {
        parent->addChild(this);
    }
}
/** @brief remove from parent
 * @note 
 */
void Widget::unlinkParent()
{
    if(parent)
    {
        parent->removeChild(this);
    }

    parent =0;
}

/** @brief active designated child
 * @param HWND widget
 * @note 
 */
void Widget::activateWidget(HWND widget)
{
    if(widget)
    {
        //widget->active =1;
        sendMessage(widget, MSG_PAINT, 0,0);
    }
}
/** @brief deactive designated child
 * @param HWND widget
 * @note 
 */
void Widget::deActivateWidget(HWND widget)
{
    if(widget){
        //widget->active =0;
        sendMessage(widget, MSG_PAINT, 0,0);
    }
}

/** @brief constructor of a widget
 * @param HWND *list
 * @param HWND item
 * @note 
 * child = A <-> B ->NUL
 */
void Widget::listInsertLast(HWND *list, HWND item)
{
#ifdef DEBUG_FRGUI
    //printf("window %s add a child: %s\n", parent->name, item->name);
#endif
    Widget *t_widget;
    
    if(t_widget =getFirstItem(list))
    {
        while(t_widget->next)
        {
            t_widget =t_widget->next;
        }
        t_widget->next =item;
        item->prev =t_widget;
    }
    else
    {
        *list =item;
    }
}
/** @brief 
 * @param HWND *list
 * @param HWND item
 * @note 
 * child = A <-> B ->NUL
 */
void Widget::listRemove(HWND *list, HWND item)
{
#ifdef DEBUG_FRGUI
    //printf("window %s remove child %s\n", parent->name, item->name);
#endif
    if(item->prev)
    {
        item->prev->next =item->next;
        if(item->next)
        {
            item->next->prev =item->prev;
        }
        
        item->prev =NUL;
        item->next =NUL;
    }
    else
    {
        if(item->next)
        {
            item->next->prev =item->prev;
        }
        *list =item->next;
        
        item->prev =NUL;
        item->next =NUL;
    }
    
}

/** @brief add child window handle
 * @param Widget *widget
 * @note 
 */
void Widget::addChild(Widget *widget)
{
    listInsertLast(&childList, widget);
}
/** @brief remove child window handle
 * @param Widget *widget
 * @note 
 */
void Widget::removeChild(Widget *widget)
{
    listRemove(&childList, widget);
}
